use crate::common::{COMMON, FRAME_SIZE, FREQ_SIZE, NB_BANDS, NB_FEATURES, PITCH_BUF_SIZE, WINDOW_SIZE};
use crate::kiss_fft::{KissFFT, KissTwiddleCpx};
use crate::rnn::RNN;

pub(crate) struct Denoise {
    analysis_mem: [f32; FRAME_SIZE],
    cepstral_mem: [f32;FRAME_SIZE],
    mem_id: usize,
    synthesis_mem: [f32; FRAME_SIZE],
    pitch_buffer: [f32; PITCH_BUF_SIZE],
    last_gain: f32,
    last_period: f32,
    mem_hp_x: [f32; 2],
    lastg: [f32; NB_BANDS],
    rnn: RNN,
}

impl Denoise {
    pub fn new() -> Denoise {
        Denoise {
            analysis_mem: [0.0; FRAME_SIZE],
            cepstral_mem: [0.0; FRAME_SIZE],
            mem_id: 0,
            synthesis_mem: [0.0; FRAME_SIZE],
            pitch_buffer: [0.0; PITCH_BUF_SIZE],
            last_gain: 0.0,
            last_period: 0.0,
            mem_hp_x: [0.0; 2],
            lastg: [0.0; NB_BANDS],
            rnn: RNN::new(),
        }
    }
}

impl Denoise {

    pub(crate) fn rnn_noise_process_frame(&mut self, input: &[f32], output: &mut [f32]) {
        let mut X: [KissTwiddleCpx; FREQ_SIZE] = [KissTwiddleCpx { r: 0f32, i: 0f32 }; FREQ_SIZE];
        let mut P: [crate::kiss_fft::KissTwiddleCpx;WINDOW_SIZE] = [KissTwiddleCpx { r: 0f32, i: 0f32 }; WINDOW_SIZE];
        let mut x: [f32; FRAME_SIZE] = [0.0; FRAME_SIZE];
        let Ex: [f32; NB_BANDS] = [0f32; NB_BANDS];
        let Ep: [f32;NB_BANDS] = [0f32; NB_BANDS];
        let Exp:[ f32; NB_BANDS] = [0f32; NB_BANDS];
        let features: [f32; NB_FEATURES] = [0f32; NB_FEATURES];
        let g: [f32; NB_BANDS] = [0f32; NB_BANDS];
        let gf:[f32; FREQ_SIZE]=[1f32; FREQ_SIZE];

        let vad_pro  = 0f32;
        let mut silence: i32 = 0;
        const a_hp: [f32; 2] = [-1.99599f32, 0.99600f32];
        const b_hp: [f32; 2] = [-2f32, 1f32];

        biquad(&mut x, &mut self.mem_hp_x, input, b_hp, a_hp, FRAME_SIZE);
        silence = Self::compute_frame_features(self, &X, &P, &Ex, &Ep, &Exp, &features, &x);
    }
    fn compute_dense(denoise: &mut Denoise, input: &[f32], output: &mut [f32]) {

    }

    fn compute_gru(denoise: &mut Denoise, state: &[f32], input: &[f32]) {
    }

    fn compute_rnn(denoise: &mut Denoise, gains: &[f32], vad: &mut [f32], input: & [f32]) {
    }
    fn compute_frame_features(&mut self, X: &[KissTwiddleCpx], P: &[KissTwiddleCpx], Ex: &[f32; 22],
                              EP: &[f32; 22], Exp: &[f32; 22], features: &[f32; 42], input: &[f32]) -> i32 {
        let E = 0f32;
        let spec_variability = 0f32;
        let Ly: [f32; NB_BANDS];
        let p: [f32; WINDOW_SIZE];
        let pitch_buf: [f32; PITCH_BUF_SIZE>>1];
        let pitch_index: i32;
        let gain: f32;
        let tmp: [f32; NB_BANDS];
        let follow: f32;
        let logMax: f32;

        Self::frame_analysis(self, X, Ex, input);

        10
    }

    fn frame_analysis(self: &mut Denoise, X: &[KissTwiddleCpx], Ex: &[f32; 22], input: &[f32]) {
        let mut x: [f32; WINDOW_SIZE] = [0.0; WINDOW_SIZE];

        x[..FRAME_SIZE].copy_from_slice(&self.analysis_mem);
        x[FRAME_SIZE..].copy_from_slice(&input[..FRAME_SIZE]);

        self.analysis_mem.copy_from_slice(&input[..FRAME_SIZE]);

        apply_window(&mut x);
        forward_transform(X, &x);

        compute_band_energy(&Ex, &X);
    }
}

fn compute_band_energy(p0: &&[f32; 22], p1: &&[KissTwiddleCpx]) {
    todo!()
}

fn forward_transform(output: &[KissTwiddleCpx], input: &[f32]) {
    let mut x: [KissTwiddleCpx;WINDOW_SIZE] = [KissTwiddleCpx { r: 0f32, i: 0f32 }; WINDOW_SIZE];
    let mut y: [KissTwiddleCpx;WINDOW_SIZE] = [KissTwiddleCpx { r: 0f32, i: 0f32 }; WINDOW_SIZE];

    for i in 0..WINDOW_SIZE {
        x[i].r = input[i];
        x[i].i = 0f32;
    }

    let common = &COMMON;
    common.kiss_fft.kiss_fft(&x, &mut y);
}

fn apply_window(x: &mut [f32]) {
    for i in 0..FRAME_SIZE {
        x[i] *= COMMON.half_window[i];
        x[WINDOW_SIZE - 1 - i] *= COMMON.half_window[i];
    }
}

fn biquad(y: &mut [f32], mem: &mut [f32; 2], x: &[f32], b: [f32; 2], a: [f32; 2], N: usize) {
    let mut xi = 0f32;
    let mut yi = 0f32;
    for i in 0.. N {
        xi = x[i];
        yi = x[i] + mem[0];
        mem[0] = mem[1] + ((b[0] * xi) - (a[0] * yi));
        mem[1] = ((b[1] * xi) - (a[1] * yi));
        y[i] = yi;
    }
}

